﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Mvc;
using SlamCms.Common;

namespace SlamCms.Web.Mvc
{
	public class HtmlGrid<T> : IHtmlString where T : class
	{
		private HtmlHelper htmlHelper;
		private TagBuilder table = new TagBuilder("table");
		private IEnumerable<T> model;
		private HtmlGridColumnsConfiguration<T> columnsConfiguration = new HtmlGridColumnsConfiguration<T>();

		public HtmlGrid(HtmlHelper htmlHelper, IEnumerable<T> model)
		{
			this.htmlHelper = htmlHelper;
			this.model = model;
		}

		public HtmlGrid<T> AddClass(string value)
		{
			table.AddCssClass(value);
			return this;
		}

		public HtmlGrid<T> Columns(Action<HtmlGridColumnsConfiguration<T>> columnsConfigurator)
		{
			Ensure.ArgumentNotNull(columnsConfiguration, "columnsConfiguration");
			columnsConfigurator.Invoke(this.columnsConfiguration);
			return this;
		}

		public string ToHtmlString()
		{
			var columns = columnsConfiguration.GetColumns();

			// first header columns
			var sb = new StringBuilder();
			sb.Append("<thead>");
			sb.Append("<tr>");

			foreach (var column in columns)
				sb.Append("<th>{0}</th>".F(column.DisplayName));

			sb.Append("</tr>");
			sb.Append("</thead>");

			// now the values
			sb.Append("<tbody>");
			foreach (var item in model)
			{
				sb.Append("<tr>");

				foreach (var column in columns)
					sb.Append("<td>{0}</td>".F(column.GetValue(item)));

				sb.Append("</tr>");
			}
			
			sb.Append("</tbody>");
			table.InnerHtml = sb.ToString();

			return table.ToString(TagRenderMode.Normal);
		}
	}

	public class HtmlGridColumnsConfiguration<T> where T : class
	{
		private readonly IDictionary<string, HtmlGridColumnConfiguration<T>> columns = new Dictionary<string, HtmlGridColumnConfiguration<T>>();

		internal HtmlGridColumnsConfiguration()
		{
			LoadColumns();
		}

		protected void LoadColumns()
		{
			var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
			foreach (var property in properties)
			{
				columns.Add(property.Name, new HtmlGridColumnConfiguration<T>(property));	
			}
		}

		public HtmlGridColumnConfiguration<T> Column(string name)
		{
			return columns[name];
		}

		public HtmlGridColumnConfiguration<T> Column<TProperty>(Expression<Func<T, TProperty>> property)
		{
			var propertyName = property.GetNameFor();
			return Column(propertyName);
		}

		internal IEnumerable<HtmlGridColumnConfiguration<T>> GetColumns()
		{
			return columns.Values.Where(c => !c.IsHidden);
		}
	}

	public class HtmlGridColumnConfiguration<T>
	{
		internal HtmlGridColumnConfiguration(PropertyInfo propertyInfo)
		{
			this.PropertyInfo = propertyInfo;
			this.Name = propertyInfo.Name;
			this.DisplayName = this.Name;
		}

		public bool IsHidden { get; set; }
		public string Name { get; private set; }
		public string DisplayName { get; set; }
		internal PropertyInfo PropertyInfo { get; set; }
		public Func<T, string> Renderer { get; set; }

		public HtmlGridColumnConfiguration<T> SetDisplayName(string displayName)
		{
			this.DisplayName = displayName;
			return this;
		}

		public HtmlGridColumnConfiguration<T> SetRenderer(Func<T, string> renderer)
		{
			this.Renderer = renderer;
			return this;
		}

		public HtmlGridColumnConfiguration<T> Hidden()
		{
			this.IsHidden = true;
			return this;
		}

		internal string GetValue(T instance)
		{
			if (Renderer != null)
				return Renderer(instance);

			var value = PropertyInfo.GetValue(instance, null);

			if (value == null)
				return null;

			return value.ToString();
		}
	}
}
